﻿using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Input;

namespace SpectrEngine.Framework.Input
{
    /// <summary>
    /// Game pad button enumeration.
    /// </summary>
    public enum GamePadButtons
    {
        A,
        B,
        Back,
        BigButton,
        LeftShoulder,
        LeftStick,
        RightShoulder,
        RightStick,
        Start,
        X,
        Y
    }

    /// <summary>
    /// Game pad directional pad enumeration.
    /// </summary>
    public enum GamePadDPads
    {
        Down,
        Left,
        Right,
        Up
    }

    /// <summary>
    /// Game pad thumb stick enumeration.
    /// </summary>
    public enum GamePadThumbSticks
    {
        Left,
        Right
    }

    /// <summary>
    /// Game pad trigger enumeration.
    /// </summary>
    public enum GamePadTriggers
    {
        Left,
        Right
    }

    /// <summary>
    /// Game pad manager.
    /// </summary>
    public static class GamePadManager
    {
        /// <summary>
        /// Previous game pad states.
        /// </summary>
        private static GamePadState[] previousStates;
        /// <summary>
        /// Current game pad states.
        /// </summary>
        private static GamePadState[] currentStates;

        /// <summary>
        /// Trigger theshold value.
        /// </summary>
        public static float TriggerThreshold { get; set; }

        /// <summary>
        /// Static constructor.
        /// </summary>
        static GamePadManager()
        {
            previousStates = new GamePadState[4];
            currentStates = new GamePadState[4];

            currentStates[0] = GamePad.GetState(PlayerIndex.One);
            currentStates[1] = GamePad.GetState(PlayerIndex.Two);
            currentStates[2] = GamePad.GetState(PlayerIndex.Three);
            currentStates[3] = GamePad.GetState(PlayerIndex.Four);
        }

        /// <summary>
        /// Update manager states.
        /// </summary>
        /// <param name="gameTime"></param>
        public static void Update()
        {
            currentStates.CopyTo(previousStates, 0);

            currentStates[0] = GamePad.GetState(PlayerIndex.One);
            currentStates[1] = GamePad.GetState(PlayerIndex.Two);
            currentStates[2] = GamePad.GetState(PlayerIndex.Three);
            currentStates[3] = GamePad.GetState(PlayerIndex.Four);
        }

        /// <summary>
        /// Gets whether or not a button is down.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="gamePadButton"></param>
        /// <returns></returns>
        public static bool IsButtonDown(PlayerIndex playerIndex, GamePadButtons gamePadButton)
        {
            switch (gamePadButton)
            {
                case GamePadButtons.A:
                    return currentStates[(int)playerIndex].Buttons.A == ButtonState.Pressed;
                case GamePadButtons.B:
                    return currentStates[(int)playerIndex].Buttons.B == ButtonState.Pressed;
                case GamePadButtons.Back:
                    return currentStates[(int)playerIndex].Buttons.Back == ButtonState.Pressed;
                case GamePadButtons.BigButton:
                    return currentStates[(int)playerIndex].Buttons.BigButton == ButtonState.Pressed;
                case GamePadButtons.LeftShoulder:
                    return currentStates[(int)playerIndex].Buttons.LeftShoulder == ButtonState.Pressed;
                case GamePadButtons.LeftStick:
                    return currentStates[(int)playerIndex].Buttons.LeftStick == ButtonState.Pressed;
                case GamePadButtons.RightShoulder:
                    return currentStates[(int)playerIndex].Buttons.RightShoulder == ButtonState.Pressed;
                case GamePadButtons.RightStick:
                    return currentStates[(int)playerIndex].Buttons.RightStick == ButtonState.Pressed;
                case GamePadButtons.Start:
                    return currentStates[(int)playerIndex].Buttons.Start == ButtonState.Pressed;
                case GamePadButtons.X:
                    return currentStates[(int)playerIndex].Buttons.X == ButtonState.Pressed;
                case GamePadButtons.Y:
                    return currentStates[(int)playerIndex].Buttons.Y == ButtonState.Pressed;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a button is up.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="gamePadButtons"></param>
        /// <returns></returns>
        public static bool IsButtonUp(PlayerIndex playerIndex, GamePadButtons gamePadButtons)
        {
            return !IsButtonPressed(playerIndex, gamePadButtons);
        }

        /// <summary>
        /// Gets whether or not a button is pressed.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="gamePadButtons"></param>
        /// <returns></returns>
        public static bool IsButtonPressed(PlayerIndex playerIndex, GamePadButtons gamePadButtons)
        {
            switch (gamePadButtons)
            {
                case GamePadButtons.A:
                    return previousStates[(int)playerIndex].Buttons.A == ButtonState.Released && currentStates[(int)playerIndex].Buttons.A == ButtonState.Pressed;
                case GamePadButtons.B:
                    return previousStates[(int)playerIndex].Buttons.B == ButtonState.Released && currentStates[(int)playerIndex].Buttons.B == ButtonState.Pressed;
                case GamePadButtons.Back:
                    return previousStates[(int)playerIndex].Buttons.Back == ButtonState.Released && currentStates[(int)playerIndex].Buttons.Back == ButtonState.Pressed;
                case GamePadButtons.BigButton:
                    return previousStates[(int)playerIndex].Buttons.BigButton == ButtonState.Released && currentStates[(int)playerIndex].Buttons.BigButton == ButtonState.Pressed;
                case GamePadButtons.LeftShoulder:
                    return previousStates[(int)playerIndex].Buttons.LeftShoulder == ButtonState.Released && currentStates[(int)playerIndex].Buttons.LeftShoulder == ButtonState.Pressed;
                case GamePadButtons.LeftStick:
                    return previousStates[(int)playerIndex].Buttons.LeftStick == ButtonState.Released && currentStates[(int)playerIndex].Buttons.LeftStick == ButtonState.Pressed;
                case GamePadButtons.RightShoulder:
                    return previousStates[(int)playerIndex].Buttons.RightShoulder == ButtonState.Released && currentStates[(int)playerIndex].Buttons.RightShoulder == ButtonState.Pressed;
                case GamePadButtons.RightStick:
                    return previousStates[(int)playerIndex].Buttons.RightStick == ButtonState.Released && currentStates[(int)playerIndex].Buttons.RightStick == ButtonState.Pressed;
                case GamePadButtons.Start:
                    return previousStates[(int)playerIndex].Buttons.Start == ButtonState.Released && currentStates[(int)playerIndex].Buttons.Start == ButtonState.Pressed;
                case GamePadButtons.X:
                    return previousStates[(int)playerIndex].Buttons.X == ButtonState.Released && currentStates[(int)playerIndex].Buttons.X == ButtonState.Pressed;
                case GamePadButtons.Y:
                    return previousStates[(int)playerIndex].Buttons.Y == ButtonState.Released && currentStates[(int)playerIndex].Buttons.Y == ButtonState.Pressed;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a button is released.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="gamePadButtons"></param>
        /// <returns></returns>
        public static bool IsButtonReleased(PlayerIndex playerIndex, GamePadButtons gamePadButtons)
        {
            switch (gamePadButtons)
            {
                case GamePadButtons.A:
                    return previousStates[(int)playerIndex].Buttons.A == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.A == ButtonState.Released;
                case GamePadButtons.B:
                    return previousStates[(int)playerIndex].Buttons.B == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.B == ButtonState.Released;
                case GamePadButtons.Back:
                    return previousStates[(int)playerIndex].Buttons.Back == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.Back == ButtonState.Released;
                case GamePadButtons.BigButton:
                    return previousStates[(int)playerIndex].Buttons.BigButton == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.BigButton == ButtonState.Released;
                case GamePadButtons.LeftShoulder:
                    return previousStates[(int)playerIndex].Buttons.LeftShoulder == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.LeftShoulder == ButtonState.Released;
                case GamePadButtons.LeftStick:
                    return previousStates[(int)playerIndex].Buttons.LeftStick == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.LeftStick == ButtonState.Released;
                case GamePadButtons.RightShoulder:
                    return previousStates[(int)playerIndex].Buttons.RightShoulder == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.RightShoulder == ButtonState.Released;
                case GamePadButtons.RightStick:
                    return previousStates[(int)playerIndex].Buttons.RightStick == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.RightStick == ButtonState.Released;
                case GamePadButtons.Start:
                    return previousStates[(int)playerIndex].Buttons.Start == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.Start == ButtonState.Released;
                case GamePadButtons.X:
                    return previousStates[(int)playerIndex].Buttons.X == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.X == ButtonState.Released;
                case GamePadButtons.Y:
                    return previousStates[(int)playerIndex].Buttons.Y == ButtonState.Pressed && currentStates[(int)playerIndex].Buttons.Y == ButtonState.Released;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a game pad is connected.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public static bool IsConnected(PlayerIndex playerIndex)
        {
            return currentStates[(int)playerIndex].IsConnected;
        }

        /// <summary>
        /// Gets whether or not a game pad was just connected.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public static bool WasConnected(PlayerIndex playerIndex)
        {
            return !previousStates[(int)playerIndex].IsConnected && currentStates[(int)playerIndex].IsConnected;
        }

        /// <summary>
        /// Gets whether or not a game pad was just disconnected.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <returns></returns>
        public static bool WasDisconnected(PlayerIndex playerIndex)
        {
            return previousStates[(int)playerIndex].IsConnected && !currentStates[(int)playerIndex].IsConnected;
        }

        /// <summary>
        /// Gets whether or not a directional button is up.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="dPad"></param>
        /// <returns></returns>
        public static bool IsDPadUp(PlayerIndex playerIndex, GamePadDPads dPad)
        {
            int index = (int)playerIndex;

            switch (dPad)
            {
                case GamePadDPads.Down:
                    return currentStates[index].DPad.Down == ButtonState.Released;
                case GamePadDPads.Left:
                    return currentStates[index].DPad.Left == ButtonState.Released;
                case GamePadDPads.Right:
                    return currentStates[index].DPad.Right == ButtonState.Released;
                case GamePadDPads.Up:
                    return currentStates[index].DPad.Up == ButtonState.Released;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a directional button is down.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="dPad"></param>
        /// <returns></returns>
        public static bool IsDPadDown(PlayerIndex playerIndex, GamePadDPads dPad)
        {
            return !IsDPadUp(playerIndex, dPad);
        }

        /// <summary>
        /// Gets whether or not a directional button has been pressed.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="dPad"></param>
        /// <returns></returns>
        public static bool IsDPadPressed(PlayerIndex playerIndex, GamePadDPads dPad)
        {
            int index = (int)playerIndex;

            switch (dPad)
            {
                case GamePadDPads.Down:
                    return previousStates[index].DPad.Down == ButtonState.Released && currentStates[index].DPad.Down == ButtonState.Pressed;
                case GamePadDPads.Left:
                    return previousStates[index].DPad.Left == ButtonState.Released && currentStates[index].DPad.Left == ButtonState.Pressed;
                case GamePadDPads.Right:
                    return previousStates[index].DPad.Right == ButtonState.Released && currentStates[index].DPad.Right == ButtonState.Pressed;
                case GamePadDPads.Up:
                    return previousStates[index].DPad.Up == ButtonState.Released && currentStates[index].DPad.Up == ButtonState.Pressed;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a directional button has been released.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="dPad"></param>
        /// <returns></returns>
        public static bool IsDPadReleased(PlayerIndex playerIndex, GamePadDPads dPad)
        {
            int index = (int)playerIndex;

            switch (dPad)
            {
                case GamePadDPads.Down:
                    return previousStates[index].DPad.Down == ButtonState.Pressed && currentStates[index].DPad.Down == ButtonState.Released;
                case GamePadDPads.Left:
                    return previousStates[index].DPad.Left == ButtonState.Pressed && currentStates[index].DPad.Left == ButtonState.Released;
                case GamePadDPads.Right:
                    return previousStates[index].DPad.Right == ButtonState.Pressed && currentStates[index].DPad.Right == ButtonState.Released;
                case GamePadDPads.Up:
                    return previousStates[index].DPad.Up == ButtonState.Pressed && currentStates[index].DPad.Up == ButtonState.Released;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets the position of a thumbstick.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="thumbStick"></param>
        /// <returns></returns>
        public static Vector2 GetThumbstick(PlayerIndex playerIndex, GamePadThumbSticks thumbStick)
        {
            int index = (int)playerIndex;

            switch (thumbStick)
            {
                case GamePadThumbSticks.Left:
                    return currentStates[index].ThumbSticks.Left;
                case GamePadThumbSticks.Right:
                    return currentStates[index].ThumbSticks.Right;
                default:
                    return Vector2.Zero;
            }
        }

        /// <summary>
        /// Gets the change in position of a thumbstick.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="thumbStick"></param>
        /// <returns></returns>
        public static Vector2 GetThumbstickChange(PlayerIndex playerIndex, GamePadThumbSticks thumbStick)
        {
            int index = (int)playerIndex;

            switch (thumbStick)
            {
                case GamePadThumbSticks.Left:
                    return currentStates[index].ThumbSticks.Left - previousStates[index].ThumbSticks.Left;
                case GamePadThumbSticks.Right:
                    return currentStates[index].ThumbSticks.Right - previousStates[index].ThumbSticks.Right;
                default:
                    return Vector2.Zero;
            }
        }

        /// <summary>
        /// Gets the value of a trigger.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public static float GetTrigger(PlayerIndex playerIndex, GamePadTriggers trigger)
        {
            int index = (int)playerIndex;

            switch (trigger)
            {
                case GamePadTriggers.Left:
                    return currentStates[index].Triggers.Left;
                case GamePadTriggers.Right:
                    return currentStates[index].Triggers.Right;
                default:
                    return 0.0f;
            }
        }

        /// <summary>
        /// Gets the change in value of a trigger.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public static float GetTriggerChange(PlayerIndex playerIndex, GamePadTriggers trigger)
        {
            int index = (int)playerIndex;

            switch (trigger)
            {
                case GamePadTriggers.Left:
                    return currentStates[index].Triggers.Left - previousStates[index].Triggers.Left;
                case GamePadTriggers.Right:
                    return currentStates[index].Triggers.Right - previousStates[index].Triggers.Right;
                default:
                    return 0.0f;
            }
        }

        /// <summary>
        /// Gets whether or not a trigger is up.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public static bool IsTriggerUp(PlayerIndex playerIndex, GamePadTriggers trigger)
        {
            int index = (int)playerIndex;

            switch (trigger)
            {
                case GamePadTriggers.Left:
                    return currentStates[index].Triggers.Left < TriggerThreshold;
                case GamePadTriggers.Right:
                    return currentStates[index].Triggers.Right < TriggerThreshold;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a trigger is down.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public static bool IsTriggerDown(PlayerIndex playerIndex, GamePadTriggers trigger)
        {
            int index = (int)playerIndex;

            switch (trigger)
            {
                case GamePadTriggers.Left:
                    return currentStates[index].Triggers.Left >= TriggerThreshold;
                case GamePadTriggers.Right:
                    return currentStates[index].Triggers.Right >= TriggerThreshold;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a trigger has been pressed.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public static bool IsTriggerPressed(PlayerIndex playerIndex, GamePadTriggers trigger)
        {
            int index = (int)playerIndex;

            switch (trigger)
            {
                case GamePadTriggers.Left:
                    return currentStates[index].Triggers.Left >= TriggerThreshold && previousStates[index].Triggers.Left < TriggerThreshold;
                case GamePadTriggers.Right:
                    return currentStates[index].Triggers.Right >= TriggerThreshold && previousStates[index].Triggers.Right < TriggerThreshold;
                default:
                    return false;
            }
        }

        /// <summary>
        /// Gets whether or not a trigger has been released.
        /// </summary>
        /// <param name="playerIndex"></param>
        /// <param name="trigger"></param>
        /// <returns></returns>
        public static bool IsTriggerReleased(PlayerIndex playerIndex, GamePadTriggers trigger)
        {
            int index = (int)playerIndex;

            switch (trigger)
            {
                case GamePadTriggers.Left:
                    return currentStates[index].Triggers.Left < TriggerThreshold && previousStates[index].Triggers.Left >= TriggerThreshold;
                case GamePadTriggers.Right:
                    return currentStates[index].Triggers.Right < TriggerThreshold && previousStates[index].Triggers.Right >= TriggerThreshold;
                default:
                    return false;
            }
        }
    }
}
